<?php
declare (strict_types=1);

namespace addons\BuilderPlus\contract;

use addons\BuilderPlus\exception\BuilderException;
use addons\BuilderPlus\facade\Table;
use DateTime;
use think\Model;
use think\model\concern\SoftDelete;

class BaseModel extends Model
{
    use SoftDelete;

    protected $deleteTime = 'delete_time';

    protected $defaultSoftDelete = 0;

    protected function readTransform($value, array|string $type)
    {
        return empty($value) ? '' : parent::readTransform($value, $type);
    }

    public static function getTableList(array $param, array $options): array
    {
        try {

            $arguments = Table::getCache($param['builderTableToken']);

            if (empty($arguments)) {
                throw new BuilderException('页面加载失败，请尝试刷新页面后重试！');
            }

            if (!empty($param['getTableArguments'])) {
                return self::getTableArguments($arguments);
            }


            $that = parent::field('*');
            if (!empty($param['tableSort'])) {
                [$field, $order] = explode(',', $param['tableSort']);
                $withSort = self::matchWithSort($arguments['columns'], $field);

                $withSort ? $that->hasWhere($withSort, function ($query) use ($field, $order) {
                    $query->order($field, $order);
                })->orderRaw(" `{$withSort}`.{$field} {$order}") : $that->order($field, $order);
            } else {
                $that->order($arguments['sortType']);
            }
            if (!empty($options['with'])) $that->with($options['with']);

            if (!empty($param['searchOpen'])) {
                if (!empty($param['exportCheckIds']) && !empty($param['tableExport'])) {
                    $that->whereIn($options['pk'], $param['exportCheckIds']);
                } else {
                    $that = self::processSearch($that, $param, $arguments);
                }
            }

            if (!empty($arguments['specifyQuery'])) {
                $that->where($arguments['specifyQuery']);
            }

            if (!empty($arguments['tree'])) {
                $key = !empty($options['tree']['pid']) ? $options['tree']['pid'] : 'pid';
                $that->where($key, !empty($param['parentId']) ? $param['parentId'] : 0);
            }

            if (!empty($options['append'])) $that->append($options['append']);

            $count = 0;
            if (empty($param['tableExport'])) {
                $count = getCount($that->fetchSql()->select());
                $that->page(intval($param['page'] ?? 1), intval($param['limit'] ?? 10));
            } else {
                ini_set('memory_limit', $options['memory_limit'] ?? '1024M');
            }
            $list = $that->select();
            return ['code' => 0, 'data' => $list, 'count' => $count, 'census' => []];

        } catch (\Exception $e) {

            return ['code' => -1, 'msg' => $e->getMessage()];
        }
    }

    public static function matchWithSort(array $columns, string $field): string
    {
        foreach ($columns as $column) {
            if ($field == $column['field'] && !empty($column['withSort'])) {
                return $column['withSort'];
            }
        }
        return '';
    }

    public static function getTableArguments($arguments): array
    {
        return ['code' => 1, 'msg' => 'success', 'data' => [
            'top_buttons'   => $arguments['top_buttons'],
            'right_buttons' => $arguments['right_buttons'],
            'search_items'  => $arguments['search_items'],
            'columns'       => $arguments['columns'],
            'switch'        => $arguments['switch'],
            'fastSort'      => $arguments['fastSort'],
            'totalRow'      => $arguments['totalRow'],
            'tree'          => $arguments['tree'],
            'treeConfig'    => $arguments['treeConfig'],
            'url'           => $arguments['url'],
            'token'         => $arguments['token'],
            'menu'          => $arguments['menu'],
            'pk'            => $arguments['builderPk']
        ]];
    }

    public static function processSearch($that, $param, $arguments)
    {
        try {

            foreach ($arguments['search_items'] as $items) {
                $field = $items['field'];
                $fieldVal = '';
                if ($items['type'] != 'linkage') {
                    if(empty($param[$items['field']])){
                        continue;
                    }
                    $fieldVal = $param[$items['field']];
                }
                $type = $items['type'];
                $condition = $items['condition'];
                switch ($type) {
                    case "linkage":
                        $linkageField = explode('-', $field);
                        foreach ($linkageField as $item) {
                            if(!empty($param[$item])){
                                $that->where($item, $condition, $param[$item]);
                            }
                        }
                        break;
                    case "date":
                        $field = strpos($field, "__") ? str_replace("__", "|", $field) : $field;
                        if ($items['dateRange']) {
                            [$start, $end] = explode(' - ', $fieldVal);
                            [$start, $end] = match ($items['dateType']) {
                                'year' => [$start . '-01-01', (new DateTime("$end-12-31"))
                                    ->format('Y-m-d')
                                ],
                                'month' => [$start . '-01', (new DateTime("$end-01"))
                                    ->modify('last day of this month')
                                    ->format('Y-m-d')
                                ],
                                'time' => [date('Y-m-d ') . $start, date('Y-m-d ') . $end],
                                default => [$start, $end]
                            };
                        } else {
                            $start = $fieldVal;
                            $end = $fieldVal;
                        }
                        $extra = in_array($items['dateType'], ['time', 'datetime']) ? 0 : 86399;
                        $that->whereTime($field, 'between', [strtotime($start), strtotime($end) + $extra]);
                        break;
                    default:
                        $field = strpos($field, "__") ? str_replace("__", "|", $field) : $field;
                        if ($items['relationClass'] && $items['relationField']) {
                            [$m, $f] = explode(',', $items['relationField']);
                            if (in_array($condition, ['=', '<>', '>', '>=', '<', '<='])) {
                                $hasThat = $items['relationClass']::where($m, $condition, $fieldVal);

                            } else {
                                $method = 'where' . ucfirst($condition);
                                $hasThat = match ($condition) {
                                    'Like', 'NotLike' => $items['relationClass']::{$method}($m, '%' . $fieldVal . '%'),
                                    default => $items['relationClass']::{$method}($m, $fieldVal)
                                };
                            }
                            $that->whereIn($field, $hasThat->column($f));
                            break;
                        } else {
                            if (in_array($condition, ['=', '<>', '>', '>=', '<', '<='])) {
                                $that->where($field, $condition, $fieldVal);
                            } else {
                                $method = 'where' . ucfirst($condition);
                                match ($condition) {
                                    'Like', 'NotLike' => $that->{$method}($field, '%' . $fieldVal . '%'),
                                    default => $that->{$method}($field, $fieldVal)
                                };
                            }
                        }
                }
            }

            return $that;
        } catch (\Exception $e) {
            throw new BuilderException($e->getMessage());
        }
    }

}
